package demo;

import cn.itcast.demo.pojo.Goods;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ElasticDemo {
    private RestHighLevelClient client;
    @Before
    public void init(){
        client=new RestHighLevelClient
                (RestClient.builder
                        (new HttpHost("192.168.206.99",9200,"http")));

    }
    @Test
    public void testCreateIndex() throws IOException {
        // 1.创建CreateIndexRequest对象，并指定索引库名称
        CreateIndexRequest request = new CreateIndexRequest("goods");
        try {
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("goods");
            client.indices().delete(deleteIndexRequest,RequestOptions.DEFAULT);
        } catch (IOException e) {

        }
        // 2.准备参加索引的JSON请求体，包括settings和mapping
        request.source("{\n" +
                "  \"settings\": {\n" +
                "    \"analysis\": {\n" +
                "      \"analyzer\": {\n" +
                "        \"my_pinyin\": {\n" +
                "          \"tokenizer\": \"ik_smart\",\n" +
                "          \"filter\": [\n" +
                "            \"py\"\n" +
                "          ]\n" +
                "        }\n" +
                "      },\n" +
                "      \"filter\": {\n" +
                "        \"py\": {\n" +
                "          \"type\": \"pinyin\",\n" +
                "          \"keep_full_pinyin\": false,\n" +
                "          \"keep_joined_full_pinyin\": true,\n" +
                "          \"keep_original\": true,\n" +
                "          \"limit_first_letter_length\": 16,\n" +
                "          \"remove_duplicated_term\": true\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  },\n" +
                "  \"mappings\": {\n" +
                "    \"properties\": {\n" +
                "      \"id\": {\n" +
                "        \"type\": \"keyword\"\n" +
                "      },\n" +
                "      \"name\": {\n" +
                "        \"type\": \"completion\",\n" +
                "        \"analyzer\": \"my_pinyin\",\n" +
                "        \"search_analyzer\": \"ik_smart\"\n" +
                "      },\n" +
                "      \"title\":{\n" +
                "        \"type\": \"text\",\n" +
                "        \"analyzer\": \"my_pinyin\",\n" +
                "        \"search_analyzer\": \"ik_smart\"\n" +
                "      },\n" +
                "      \"price\":{\n" +
                "        \"type\": \"long\"\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}", XContentType.JSON);

        // 3.发起请求，得到响应
        CreateIndexResponse response=client.indices().create(request, RequestOptions.DEFAULT);
        System.out.println("response = " + response.isAcknowledged());
    }

    @Test
    public void testBulkDocument() throws IOException {
        // 1.准备文档数据
        List<Goods> list = new ArrayList<>();
        list.add(new Goods(1L, "红米9", "红米9手机 数码", 1499L));
        list.add(new Goods(2L, "三星 Galaxy A90", "三星 Galaxy A90 手机 数码 疾速5G 骁龙855", 3099L));
        list.add(new Goods(3L, "Sony WH-1000XM3", "Sony WH-1000XM3 降噪耳机 数码", 2299L));
        list.add(new Goods(4L, "松下剃须刀", "松下电动剃须刀高转速磁悬浮马达", 599L));
        // 2.创建BulkRequest对象
        BulkRequest bulkrequest = new BulkRequest();
        // 3.创建多个IndexRequest对象，并添加到BulkRequest中
        for (Goods goods : list) {
            bulkrequest.add(new IndexRequest("goods")
                    .id(goods.getId().toString())
                    .source(JSON.toJSONString(goods),XContentType.JSON)
            );
        }
        // 4.发起请求
        BulkResponse response = client.bulk(bulkrequest, RequestOptions.DEFAULT);
        System.out.println("response = " + response.status());
    }

    @Test
    public void testBasicSearchWithSortAndPage() throws IOException, InvocationTargetException, IllegalAccessException {
        // 1.创建SearchSourceBuilder对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 1.0.source过滤
        searchSourceBuilder.fetchSource(new String[0],new String[]{"name"});
        // 1.1.添加查询条件QueryBuilders，这里选择布尔查询，查询标题包含“数码”，
        // 并且价格小于3000
        // 1.1.1.定义布尔查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 1.1.2.添加match查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("title","数码"));
        // 1.1.3.添加价格过滤
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(3000));
        // 1.2.添加排序、分页等其它条件
        searchSourceBuilder.sort("price", SortOrder.ASC);
        // 1.3.添加分页条件
        int page=1;int size=5;
        int form=(page-1)*size;
        searchSourceBuilder.size(size);
        searchSourceBuilder.from(form);
        // 1.4.高亮
        searchSourceBuilder.highlighter(new HighlightBuilder().field("title"));
        // 2.创建SearchRequest对象，并制定索引库名称
        SearchRequest searchRequest = new SearchRequest("goods");
        // 2.1.添加SearchSourceBuilder对象到SearchRequest对象中
        searchRequest.source(searchSourceBuilder);
        // 3.发起请求，得到结果
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        // 4.解析结果
        SearchHits searchHits = searchResponse.getHits();
        //  4.1.获取总条数
        TotalHits totalHits = searchHits.getTotalHits();
        System.out.println("总条数"+totalHits);
        //  4.2.获取SearchHits数组，并遍历
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            //  - 获取其中的`_source`，是JSON数据
            String json = hit.getSourceAsString();
            //  - 把`_source`反序列化为User对象
            Goods goods = JSON.parseObject(json, Goods.class);
            // 获取高亮结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            // 遍历高亮字段
            for (HighlightField field : highlightFields.values()) {
                // 获取字段名
                String name = field.getName();
                // 获取字段值
                String fieldValue = StringUtils.join(field.getFragments());
                // 注入对象中
                BeanUtils.setProperty(goods, name, fieldValue);

            }
        }
    }

    //自动补全
    @Test
    public void testSuggest() throws IOException {
        //创建查询条件工厂（封装查询条件）的对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 1.1.准备Suggest，需要指定四个内容：
        // 1）自动补全的名称：name_suggest
        // 2）自动补全的类型：SuggestBuilders.completionSuggestion
        // 3）自动补全的字段：completionSuggestion("name")
        // 4）自动补全的前缀：.prefix("s")
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        suggestBuilder.addSuggestion("name_suggest", SuggestBuilders.completionSuggestion("name").prefix("s").size(3000));
        //添加suggest条件
        searchSourceBuilder.suggest(suggestBuilder);
        //构建搜索请求的对象，放入sourceBuilder
        SearchRequest request = new SearchRequest("goods");
        request.source(searchSourceBuilder);
        //发请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析结果
        Suggest suggest = response.getSuggest();
        //根据名称获取结果
        Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> namesuggest = suggest.getSuggestion("name_suggest");
        namesuggest.forEach(suggestion ->{
            //获取其中的options
            List<? extends Suggest.Suggestion.Entry.Option> options = suggestion.getOptions();
            System.out.println("补全的结果如下： ");
            for (Suggest.Suggestion.Entry.Option option : options) {
                Text text = option.getText();
                System.out.println("\t"+text);
            }
        });
    }

    //异步新增
    @Test
    public void testAsyncAddDocument() throws InterruptedException {
        // 准备文档
        Goods goods = new Goods(5L, "松下电吹风", "松下电吹风 网红电吹风", 1599L);

        // 创建请求
        IndexRequest request = new IndexRequest("goods")
                .id(goods.getId().toString())
                .source(JSON.toJSONString(goods), XContentType.JSON);

        // 执行请求，第三个参数是回调处理
        client.indexAsync(request, RequestOptions.DEFAULT, new ActionListener<IndexResponse>() {
            /**
             * 执行成功时的回调，参数是IndexResponse结果
             * @param indexResponse 执行结果
             */
            @Override
            public void onResponse(IndexResponse indexResponse) {
                System.out.println("我是成功的回调！" + indexResponse);
            }
            /**
             * 执行失败时的回调，参数是异常信息
             * @param e 异常信息
             */
            @Override
            public void onFailure(Exception e) {
                System.out.println("我是失败的回调！");
                e.printStackTrace();
            }
        });

        System.out.println("我的异步方法调用完成~~");
        // 因为我们的程序结束会立即停止，接收不到回调结果，这里我们休眠一下，等待下回调结果
        Thread.sleep(2000L);
    }

    //异步删除
    @Test
    public void testAsyncDeleteDocument() throws InterruptedException {
        // 创建请求
        DeleteRequest request = new DeleteRequest("goods", "5");

        // 执行请求，第三个参数是回调处理
        client.deleteAsync(request, RequestOptions.DEFAULT, new ActionListener<DeleteResponse>() {
            /**
             * 执行成功时的回调，参数是IndexResponse结果
             * @param indexResponse 执行结果
             */
            @Override
            public void onResponse(DeleteResponse indexResponse) {
                System.out.println("我是成功的回调！" + indexResponse);
            }
            /**
             * 执行失败时的回调，参数是异常信息
             * @param e 异常信息
             */
            @Override
            public void onFailure(Exception e) {
                System.out.println("我是失败的回调！");
                e.printStackTrace();
            }
        });

        System.out.println("我的异步方法调用完成~~");
        // 因为我们的程序结束会立即停止，接收不到回调结果，这里我们休眠一下，等待下回调结果
        Thread.sleep(2000L);
    }

    @After
    public void close() throws IOException {
    client.close();}
}
